package cn.itcast.gateway;

import com.alibaba.fastjson.JSON;
import com.utils.JwtUtils;
import constant.CodeConstant;
import constant.MsgConstant;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import result.Result;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;

// @Order(-1)
@Component
@Slf4j
public class AuthorizeFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求路径
        String path = exchange.getRequest().getURI().getRawPath();
        log.info("请求路径："+path);

        // 1. 判断请求路径中是否以feign开头，如果是则判断请求是否来自本机
        if (path.startsWith("/feign")){
            // 表示是内部微服务之间的调用
            InetAddress localhostIPv4;
            InetAddress localhostIPv6;
            try {
                localhostIPv4 = InetAddress.getByName("127.0.0.1");
                localhostIPv6 = InetAddress.getByName("::1");
            } catch (UnknownHostException e) {
                // 这通常不会发生，除非系统配置非常特殊
                throw new RuntimeException("Unable to resolve localhost", e);
            }

            InetAddress remoteAddress = exchange.getRequest().getRemoteAddress().getAddress();
            if (localhostIPv4.equals(remoteAddress) || localhostIPv6.equals(remoteAddress)) {
                // 如果请求来自本机（无论是IPv4还是IPv6），则继续处理
                System.out.println("请求来自本机");
                return chain.filter(exchange);
            } else {
                // 如果请求不是来自本机，则返回错误响应
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.NOT_FOUND);
                DataBufferFactory bufferFactory = response.bufferFactory();
                DataBuffer buffer = bufferFactory.wrap("请求未找到或未授权".getBytes(StandardCharsets.UTF_8));
                return response.writeWith(Mono.just(buffer));
            }
        }

        // 2. 判断这个路劲中是否含有campus的开头的请求，如果有则不需要进行token校验
        if (path.startsWith("/campus")){
            // 表示是以campus开头的请求，不需要进行token校验，如果有token就加上
            // 2.1 从请求头中获取token
            ServerHttpRequest request = exchange.getRequest();
            HttpHeaders requestHeaders = request.getHeaders();
            String token = requestHeaders.getFirst("TOKEN");

            // 2.2 判断token是否为空，空表示没有token，放行
            if (token == null || token.isEmpty()){
                return chain.filter(exchange);
            }

            // 2.3 对token进行解析
            try {
                JwtUtils.parseJWT(token);
            }catch (Exception e){
                log.info("不需要token的时候解析令牌失败!");
                return chain.filter(exchange);
            }

            // 2.4 获取到token中存放的用户存放的用户id
            Claims claims = JwtUtils.parseJWT(token);
            Integer id = (Integer) claims.get("id");

            if (id == null){
                return chain.filter(exchange);
            }

            // 2.5 将用户id信息发在请求头信息中
            exchange.getRequest().mutate().headers(
                    httpHeaders -> {
                        httpHeaders.add("userId", id.toString());
                    }
            ).build();
            return chain.filter(exchange);
        }

        // 3. 从请求头中获取token
        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders requestHeaders = request.getHeaders();
        String token = requestHeaders.getFirst("TOKEN");
        ServerHttpResponse response = exchange.getResponse();

        // 4. 判断token是否为空
        if (token == null || token.isEmpty()){
            //4.1 设置状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);

            // 4.2 封装返回对象
            Result<String> result = new Result<>();
            result.setMsg(MsgConstant.TOKEN_IS_EMPTY);
            result.setCode(CodeConstant.TOKEN_IS_EMPTY);
            byte[] bytes = JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8);

            // 4.3 调用bufferFactory方法,生成DataBuffer对象
            DataBuffer buffer = response.bufferFactory().wrap(bytes);

            // 4.4 调用Mono中的just方法,返回要写给前端的JSON数据
            return response.writeWith(Mono.just(buffer));
        }

        // 5. 对token进行解析
        try {
            JwtUtils.parseJWT(token);
        }catch (Exception e){
            log.info("令牌解析失败!");
            // 5.1 设置状态码
            response.setStatusCode(HttpStatus.UNAUTHORIZED);

            // 5.2 封装返回对象
            Result<String> result = new Result<>();
            result.setMsg(MsgConstant.TOKEN_PARSING_FAILED);
            result.setCode(CodeConstant.TOKEN_PARSING_FAILED);
            byte[] bytes = JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8);

            // 5.3 调用bufferFactory方法,生成DataBuffer对象
            DataBuffer buffer = response.bufferFactory().wrap(bytes);

            // 5.4 调用Mono中的just方法,返回要写给前端的JSON数据
            return response.writeWith(Mono.just(buffer));
        }

        // 6. 获取到token中存放的用户存放的用户id
        Claims claims = JwtUtils.parseJWT(token);
        Integer id = (Integer) claims.get("id");

        if (id == null){
            // 6.1表示token中没有用户id信息，表示token解析不成功
            Result<String> result = new Result<>();
            result.setMsg(MsgConstant.TOKEN_PARSING_FAILED);
            result.setCode(CodeConstant.TOKEN_PARSING_FAILED);
            byte[] bytes = JSON.toJSONString(result).getBytes(StandardCharsets.UTF_8);

            // 6.2 调用bufferFactory方法,生成DataBuffer对象
            DataBuffer buffer = response.bufferFactory().wrap(bytes);

            // 6.3 调用Mono中的just方法,返回要写给前端的JSON数据
            return response.writeWith(Mono.just(buffer));
        }

        // 7. 将用户id信息发在请求头信息中
        exchange.getRequest().mutate().headers(
                httpHeaders -> {
                    httpHeaders.add("userId", id.toString());
                }
        ).build();

        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
